"use strict";

const express = require('express');
const methodOverride = require('method-override');
const _ = require('lodash');
const lodashId = require('lodash-id');
const low = require('lowdb');
const Memory = require('lowdb/adapters/Memory');
const FileSync = require('lowdb/adapters/FileSync');
const bodyParser = require('../body-parser');
const validateData = require('./validate-data');
const plural = require('./plural');
const nested = require('./nested');
const singular = require('./singular');
const mixins = require('../mixins');
module.exports = (db, opts) => {
  opts = Object.assign({
    foreignKeySuffix: 'Id',
    _isFake: false
  }, opts);
  if (typeof db === 'string') {
    db = low(new FileSync(db));
  } else if (!_.has(db, '__chain__') || !_.has(db, '__wrapped__')) {
    db = low(new Memory()).setState(db);
  }

  // Create router
  const router = express.Router();

  // Add middlewares
  router.use(methodOverride());
  router.use(bodyParser);
  validateData(db.getState());

  // Add lodash-id methods to db
  db._.mixin(lodashId);

  // Add specific mixins
  db._.mixin(mixins);

  // Expose database
  router.db = db;

  // Expose render
  router.render = (req, res) => {
    res.jsonp(res.locals.data);
  };

  // GET /db
  router.get('/db', (req, res) => {
    res.jsonp(db.getState());
  });

  // Handle /:parent/:parentId/:resource
  router.use(nested(opts));

  // Create routes
  db.forEach((value, key) => {
    if (key === '$schema') {
      // ignore $schema
      return;
    }
    if (_.isPlainObject(value)) {
      router.use(`/${key}`, singular(db, key, opts));
      return;
    }
    if (_.isArray(value)) {
      router.use(`/${key}`, plural(db, key, opts));
      return;
    }
    const sourceMessage = '';
    // if (!_.isObject(source)) {
    //   sourceMessage = `in ${source}`
    // }

    const msg = `Type of "${key}" (${typeof value}) ${sourceMessage} is not supported. ` + `Use objects or arrays of objects.`;
    throw new Error(msg);
  }).value();
  router.use((req, res) => {
    if (!res.locals.data) {
      res.status(404);
      res.locals.data = {};
    }
    router.render(req, res);
  });
  router.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send(err.stack);
  });
  return router;
};